#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

import os
import re
import shutil


class Util:

    @staticmethod
    def forceDelete(path):
        if os.path.islink(path):
            os.remove(path)
        elif os.path.isfile(path):
            os.remove(path)
        elif os.path.isdir(path):
            shutil.rmtree(path)
        elif os.path.lexists(path):
            os.remove(path)             # other type of file, such as device node
        else:
            pass                        # path does not exist, do nothing

    @staticmethod
    def forceSymlink(srcPath, dstPath):
        if not (os.path.islink(dstPath) and os.readlink(dstPath) == os.path.abspath(srcPath)):
            Util.forceDelete(dstPath)
            os.symlink(srcPath, dstPath)

    @staticmethod
    def forceMakeDir(path):
        if not os.path.exists(path):
            os.mkdir(path)
        elif not os.path.isdir(path):
            Util.forceDelete(path)
            os.mkdir(path)
        else:
            pass

    @staticmethod
    def forceMakeEmptyDir(path):
        if not os.path.exists(path):
            os.mkdir(path)
        elif not os.path.isdir(path):
            Util.forceDelete(path)
            os.mkdir(path)
        else:
            for fn in os.listdir(path):
                Util.forceDelete(os.path.join(path, fn))

    @staticmethod
    def forceCopy(srcPath, dstPath):
        Util.forceDelete(dstPath)
        if os.path.isfile(srcPath):
            shutil.copy(srcPath, dstPath)
        elif os.path.isdir(srcPath):
            shutil.copytree(srcPath, dstPath)
        else:
            assert False

    @staticmethod
    def encodeUnicode(string):
        # FIXME
        return string

    @staticmethod
    def decodeUnicode(string):
        # There may be a r"\\" in front of r"\x", so replace the r"\\" to r"\x005c"
        # to avoid missing matches. Example: r"C:\\users\\x1234\\\x0041\x0042CD".
        # Note the difference between r"\\x1234", r"\\\x0041" and r"\x0042".
        # It should be r"C:\users\x1234\ABCD" after decoding.
        chunks = re.split(r"\\x", string.replace(r"\\", r"\x005c"))
        out = chunks.pop(0).encode().decode("unicode_escape")
        for chunk in chunks:
            # We have seen file with unicode characters escaped on 1 byte (\xfa),
            # 1.5 bytes (\x444) and 2 bytes (\x00ed). So we try 0 padding, 1 and 2
            # (python wants its escaped sequence to be exactly on 4 characters).
            # The exception let us know if it worked or not
            for i in [0, 1, 2]:
                try:
                    out += ("\\u{}{}".format("0" * i, chunk).encode().decode("unicode_escape"))
                    break
                except UnicodeDecodeError:
                    pass
        return out
